<?php

namespace App\Http\Controllers\Api;

use Illuminate\Support\Facades\DB;

/**
 * 微信公共类文件
 * @package util
 * @createtime 2017-07-26 11:10:14
 *
 */
class WeChatController extends Controller
{
    /**********************基础配置  开始********/
    /**
     * 微信公众号AppId
     * @var string
     */
    private $appid;

    /**
     * 微信公众号AppSecret
     * @var string
     */
    private $appsecret;
    /**
     * 微信公众号验证服务器token
     * @var string
     */
    private $token;

    protected $state;
    /**
     * 微信公众号EncodingAESKey
     * @var string
     */
    private $encoding_aeskey;
    /**
     * 微信公众号商户号
     * @var string
     */
    private $mch_id;
    /**
     * 微信公众号商户支付密钥
     * @var string
     */
    private $key;
    //=======【证书路径设置】=====================================
    /**
     * TODO：设置商户证书路径
     * 证书路径,注意应该填写绝对路径（仅退款、撤销订单时需要，可登录商户平台下载，
     * API证书下载地址：https://pay.weixin.qq.com/index.php/account/api_cert，下载之前需要安装商户操作证书）
     * @var path
     */
    const SSLCERT_PATH = "\cert\apiclient_cert.pem";
    const SSLKEY_PATH = "\cert\apiclient_key.pem";
    //=======【curl代理设置】===================================
    /**
     * TODO：这里设置代理机器，只有需要代理的时候才设置，不需要代理，请设置为0.0.0.0和0
     * 本例程通过curl使用HTTP POST方法，此处可修改代理服务器，
     * 默认CURL_PROXY_HOST=0.0.0.0和CURL_PROXY_PORT=0，此时不开启代理（如有需要才设置）
     * @var unknown_type
     */
    const CURL_PROXY_HOST = "0.0.0.0";//"10.152.18.220";
    const CURL_PROXY_PORT = 0;//8080;
    //=======【上报信息配置】===================================
    /**
     * TODO：接口调用上报等级，默认紧错误上报（注意：上报超时间为【1s】，上报无论成败【永不抛出异常】，
     * 不会影响接口调用流程），开启上报之后，方便微信监控请求调用的质量，建议至少
     * 开启错误上报。
     * 上报等级，0.关闭上报; 1.仅错误出错上报; 2.全量上报
     * @var int
     */
    const REPORT_LEVENL = 1;

    /**
     * 微信公共类构造方法
     * @createtime 2017-07-26 11:10:14
     * @param string $appid 微信公众号AppId
     * @param string $appsecret 微信公众号AppSecret
     * @param string $token 微信公众号验证服务器token
     * @param string $encoding_aeskey 微信公众号EncodingAESKey
     * @param string $mch_id 微信公众号商户号
     * @param string $key 微信公众号商户支付密钥
     */
    public function __construct()
    {

        $this->appid = config('app.wx_app_id');//获取配置文件中的微信公众号AppId
        $this->appsecret = config('app.wx_app_secret');//获取配置文件中的微信公众号AppSecret
        $this->token = config('app.wx_token');//获取配置文件中的微信公众号验证服务器token
        $this->encoding_aeskey = config('app.wx_encoding_aeskey');//获取配置文件中的微信公众号EncodingAESKey
        $this->mch_id = config('app.wx_mch_id');//获取配置文件中的微信公众号商户号
        $this->key = config('app.wx_key');//获取配置文件中的微信公众号商户支付密钥
        $this->state = config('app.wx_state');

    }

    /**********************基础配置  结束********/

    //验证消息
    public function valid()
    {
        $echoStr = $_GET["echostr"];
        //valid signature , option
        if ($this->checkSignature() && $echoStr) {
            echo $echoStr;
            exit;
        }
    }

    //检查签名
    private function checkSignature()
    {
        if (!$this->token) {
            throw new \Exception('TOKEN is not defined');
        }
        $signature = isset($_GET["signature"]) ? $_GET["signature"] : '';
        $timestamp = isset($_GET["timestamp"]) ? $_GET["timestamp"] : '';
        $nonce = isset($_GET["nonce"]) ? $_GET["nonce"] : '';
        $token = $this->token;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);

        if ($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }
    }





    /**********************开始开发  开始********/
    /**
     * token验证服务器
     * @createtime 2017-07-26 11:10:14
     * @param string $nonce 随机数
     * @param string $timestamp 时间戳
     * @param string $echostr 随机字符串
     * @param string $signature 微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
     * @return  echostr参数内容或false
     */
    public function token($nonce, $timestamp, $echostr, $signature)
    {
        //参数字典序排序
        $array = array();
        $array = array($nonce, $timestamp, $this->token);
        sort($array);
        //验证
        $str = sha1(implode($array));//sha1加密
        //对比验证处理好的str与signature,若确认此次GET请求来自微信服务器，请原样返回echostr参数内容，则接入生效，成为开发者成功，否则接入失败。
        if ($str == $signature && $echostr) {
            return $echostr;
        } else {
            return false;
        }

    }

    /**
     * 通过appid和appsecret获取access_token 或从缓存获取基础支持access_token
     * @createtime 2017-07-26 11:07:24
     * @param type true强制获取access_token,用于本地存储的access_token在微信服务器已过期,但在本地服务器未过期时
     * @return  string 获取到的凭证
     */
    private function access_token($type)
    {
        // access_token 应该全局存储与更新，以下代码以写入到文件中做示例
        $datas = json_decode(file_get_contents(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/Json/access_token.json"));
        if (!$type && $datas->expire_time > time()) {
            $result = array('status' => 1, 'data' => $datas->access_token);
            return $result;
        }

        //配置请求url,加入appid和appsecret参数
        // 如果是企业号用以下URL获取access_token
        // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $this->appid . '&secret=' . $this->appsecret;
        //获取请求结果,并将请求结果转换成json

        $data = $this->string_json($url);
        if (array_key_exists('errcode', $data)) {
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }

        //将access_token 写入文件
        $datas->expire_time = time() + $data['expires_in'];
        $datas->access_token = $data['access_token'];
        chmod(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/Json/access_token.json", 0777);
        $fp = fopen(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/Json/access_token.json", "w");
        fwrite($fp, json_encode($datas));
        fclose($fp);
        //返回结果
        $result = array('status' => 2, 'data' => $data['access_token']);
        return $result;
    }

    /**
     * 接口调用上限清零
     * @createtime 2017-08-30 13:44:55
     * @param post_data 请求数据
     * @return json 请求结果
     */
    public function clear_quota()
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        $post_data['appid'] = $this->appid;
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/clear_quota?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->clear_quota();
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 获取微信服务器IP地址
     * @createtime 2017-07-26 13:51:20
     * @return json ip_list 微信服务器IP地址
     */
    public function ip_list()
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=' . $access_token['data'];
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        if (array_key_exists('errcode', $data)) {
            //access_token 过期重新获取并重新回调当前函数
            if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
                $access_token = $this->access_token(true);
                return $this->ip_list();
            }
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }
        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }
    /**********************开始开发  结束********/

    /**********************自定义菜单  开始********/
    /**
     * 自定义菜单创建接口
     * @createtime
     * @param post_data 请求数据
     * @return json 请求结果
     */
    public function create_menu($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/menu/create?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->create_menu($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 自定义菜单查询接口
     * @createtime
     * @return json 菜单列表
     */
    public function get_menu()
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/menu/get?access_token=' . $access_token['data'];
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        if (array_key_exists('errcode', $data)) {
            //access_token 过期重新获取并重新回调当前函数
            if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
                $access_token = $this->access_token(true);
                return $this->get_menu();
            }
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }
        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }

    /**
     * 自定义菜单删除接口
     * @createtime
     * @return json 请求结果
     */
    public function delete_menu()
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=' . $access_token['data'];
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->delete_menu();
        }
        $status = $data['errcode'] == 0 ? 1 : 40014;
        //返回结果
        $result = array('status' => $status, 'data' => $data);
        return $result;

    }

    /**
     * 获取自定义菜单配置接口
     * @createtime
     * @return json 菜单列表
     */
    public function menu_config()
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=' . $access_token['data'];
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        if (array_key_exists('errcode', $data)) {
            //access_token 过期重新获取并重新回调当前函数
            if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
                $access_token = $this->access_token(true);
                return $this->menu_config();
            }
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }
        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }
    /**********************自定义菜单  结束********/

    /**********************消息管理  开始********/

    /**
     * 消息/事件推送处理
     */
    public function responseMsg()
    {

        /*
            获得请求时POST:XML字符串
            不能用$_POST获取，因为没有key
             */
        $poststr = file_get_contents('php://input');
        if (empty($poststr)) {
            die('');
        }
        //如果推送消息 或者推送事件存在,进行处理
        if (!empty($poststr)) {
            libxml_disable_entity_loader(true);
            $postObj = simplexml_load_string($poststr, 'SimpleXMLElement', LIBXML_NOCDATA);
            $msgType = $postObj->MsgType;
            //判断事件类型,进行对应事件类型处理

            switch ($msgType) {
                case 'text':
                    $resultStr = $this->handleText($postObj);
                    break;
                case 'image':
                    $resultStr = $this->handleImage($postObj);
                    break;
                case 'voice':
                    $resultStr = $this->handleVoice($postObj);
                    break;
                case 'video':
                    $resultStr = $this->handleVideo($postObj);
                    break;
                case 'shortvideo':
                    $resultStr = $this->handleShortVideo($postObj);
                    break;
                case 'location':
                    $resultStr = $this->handleLocation($postObj);
                    break;
                case 'link':
                    $resultStr = $this->handleLink($postObj);
                    break;
                case 'event':
                    $resultStr = $this->handleEvent($postObj);
                    break;
                default:
                    $resultStr = "Unknow msg type: " . $msgType;
                    break;
            }

            return $resultStr;
        }
    }

    /**---------------------------------------------------------接收消息-接收普通消息 开始-------------------------------------------------------------**/

    /**
     * 接收文本消息
     * @param postObj object 推送事件对象
     * @return [type] [description]
     */
    public function handleText($postObj)
    {
        $contentStr = $postObj->Content;
        return $this->responseText($postObj, $contentStr);
    }

    /**
     * 接收图片消息
     * @param postObj object 推送事件对象
     * @return [type] [description]
     */
    public function handleImage($postObj)
    {
        $contentStr = "您发送了图片,图片链接:" . $postObj->PicUrl . ",图片消息媒体id:" . $postObj->MediaId . ",消息id:" . $postObj->MsgId;
        return $this->responseText($postObj, $contentStr);
    }

    /**
     * 接收语音消息
     * @param postObj object 推送事件对象
     * @return [type] [description]
     */
    public function handleVoice($postObj)
    {
        $contentStr = "您发送了语音,语音消息媒体id:" . $postObj->MediaId . ",语音格式:" . $postObj->Format . ",消息id" . $postObj->MsgId . ",Recognition:" . $postObj->Recognition;
        return $this->responseText($postObj, $contentStr);
    }

    /**
     * 接收视频消息
     * @param postObj object 推送事件对象
     * @return [type] [description]
     */
    public function handleVideo($postObj)
    {
        $contentStr = "您发送了视频,语音消息媒体id:" . $postObj->MediaId . ",视频消息缩略图的媒体id:" . $postObj->ThumbMediaId . ",消息id" . $postObj->MsgId;
        return $this->responseText($postObj, $contentStr);
    }

    /**
     * 接收小视频消息
     * @param postObj object 推送事件对象
     * @return [type] [description]
     */
    public function handleShortVideo($postObj)
    {
        $contentStr = "您发送了小视频,语音消息媒体id:" . $postObj->MediaId . ",视频消息缩略图的媒体id:" . $postObj->ThumbMediaId . ",消息id" . $postObj->MsgId;
        return $this->responseText($postObj, $contentStr);
    }

    /**
     * 接收地理位置消息
     * @param postObj object 推送事件对象
     * @return [type] [description]
     */
    public function handleLocation($postObj)
    {
        $contentStr = "您发送了地理位置,地理位置维度:" . $postObj->Location_X . ",地理位置经度:" . $postObj->Location_Y . ",地图缩放大小:" . $postObj->Scale . ",地理位置信息:" . $postObj->Label . ",消息id:" . $postObj->MsgId;
        return $this->responseText($postObj, $contentStr);
    }

    /**
     * 接收链接消息
     * @param postObj object 推送事件对象
     * @return [type] [description]
     */
    public function handleLink($postObj)
    {
        $contentStr = "您发送了链接,消息标题:" . $postObj->Title . ",消息描述:" . $postObj->Description . ",消息链接:" . $postObj->Url . ",消息id:" . $postObj->MsgId;
        return $this->responseText($postObj, $contentStr);
    }

    /**---------------------------------------------------------接收消息-接收普通消息 结束-------------------------------------------------------------**/

    /**---------------------------------------------接收消息-接收事件推送 开始-------------------------------------------------------------**/
    /**
     * 接收事件推送
     * @param postObj object 推送事件对象
     * @return [type] [description]
     */
    public function handleEvent($postObj)
    {
        $event = $postObj->Event;
        $contentStr = '';
        switch ($event) {
            case 'subscribe'://关注
                $contentStr .= '欢迎关注南方智游移动，跨境出行上网、首选智游移动，网络覆盖全球近50个国家地区，服务覆盖各大机场、高铁、口岸等便民中心。一卡在手，畅游全球';
                break;
            case 'unsubscribe'://取消关注
                break;
            case 'scancode_waitmsg'://扫描二维码
                break;
            case 'SCAN':
                break;
            case 'LOCATION':
                break;
            case 'VIEW':
                break;
            default:
                # code...
                break;
        }
//        $contentStr = "您触发了事件:" . $event;
        return $this->responseText($postObj, $contentStr);
    }
    /**------------------------------------------------------------接收消息-接收事件推送 结束-------------------------------------------------------------**/

    /**------------------------------------------------------发送消息-被动回复用户消息 开始-------------------------------------------------------------**/
    /**
     * 推送事件回复文本消息
     * @param object 接收的推送对象
     * @param content string 回复的文本消息
     * @return [type] [description]
     */
    public function responseText($object, $content)
    {
        $textTpl = "<xml>
                    <ToUserName><![CDATA[%s]]></ToUserName>
                    <FromUserName><![CDATA[%s]]></FromUserName>
                    <CreateTime>%s</CreateTime>
                    <MsgType><![CDATA[text]]></MsgType>
                    <Content><![CDATA[%s]]></Content>
                    </xml>";
        $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $content);
        return $resultStr;
    }

    /**
     * 推送事件回复图片消息
     * @param object 接收的推送对象
     * @param MediaId 通过素材管理中的接口上传多媒体文件，得到的id
     * @return [type] [description]
     */
    public function responseImage($object, $media_id)
    {
        $textTpl = "<xml>
                    <ToUserName><![CDATA[%s]]></ToUserName>
                    <FromUserName><![CDATA[%s]]></FromUserName>
                    <CreateTime>%s</CreateTime>
                    <MsgType><![CDATA[image]]></MsgType>
                    <Image>
                    <MediaId><![CDATA[media_id]]></MediaId>
                    </Image>
                    </xml>";
        $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $media_id);
        return $resultStr;
    }

    /**
     * 推送事件回复语音消息
     * @param object 接收的推送对象
     * @param MediaId 通过素材管理中的接口上传多媒体文件，得到的id
     * @return [type] [description]
     */
    public function responseVoice($object, $media_id)
    {
        $textTpl = "<xml>
                    <ToUserName><![CDATA[%s]]></ToUserName>
                    <FromUserName><![CDATA[%s]]></FromUserName>
                    <CreateTime>%s</CreateTime>
                    <MsgType><![CDATA[voice]]></MsgType>
                    <Voice>
                    <MediaId><![CDATA[%s]]></MediaId>
                    </Voice>
                    </xml>";
        $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $media_id);
        return $resultStr;
    }

    /**
     * 推送事件回复视频消息
     * @param object 接收的推送对象
     * @param media_id 通过素材管理中的接口上传多媒体文件，得到的id
     * @param title 视频消息的标题
     * @param description 视频消息的描述
     * @return
     */
    public function responseVideo($object, $media_id, $title = '', $description = '')
    {
        $textTpl = "<xml>
                    <ToUserName><![CDATA[%s]]></ToUserName>
                    <FromUserName><![CDATA[%s]]></FromUserName>
                    <CreateTime>%s</CreateTime>
                    <MsgType><![CDATA[video]]></MsgType>
                    <Video>
                    <MediaId><![CDATA[%s]]></MediaId>
                    <Title><![CDATA[%s]]></Title>
                    <Description><![CDATA[%s]]></Description>
                    </Video> 
                    </xml>";
        $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $media_id, $title, $description);
        return $resultStr;
    }

    /**
     * 推送事件回复音乐消息
     * @param object 接收的推送对象
     * @param media_id 缩略图的媒体id，通过素材管理中的接口上传多媒体文件，得到的id
     * @param title 音乐标题
     * @param description 音乐描述
     * @param music_url 音乐链接
     * @param hqmusic_url 高质量音乐链接，WIFI环境优先使用该链接播放音乐
     * @return
     */
    public function responseMusic($object, $media_id, $title = '', $description = '', $music_url = '', $hqmusic_url = '')
    {
        $textTpl = "<xml>
                    <ToUserName><![CDATA[%s]]></ToUserName>
                    <FromUserName><![CDATA[%s]]></FromUserName>
                    <CreateTime>%s</CreateTime>
                    <MsgType><![CDATA[music]]></MsgType>
                    <Music>
                    <Title><![CDATA[TITLE]]></Title>
                    <Description><![CDATA[DESCRIPTION]]></Description>
                    <MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
                    <HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
                    <ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
                    </Music>
                    </xml>";
        $resultStr = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $title, $description, $music_url, $hqmusic_url, $media_id);
        return $resultStr;
    }

    /**
     * 推送事件回复图文消息
     * @param object 接收的推送对象
     * @param articles 图文消息数组
     * @param article_count 图文消息个数，限制为8条以内
     * @param title 图文消息标题
     * @param description 图文消息描述
     * @param pic_url 图片链接，支持JPG、PNG格式，较好的效果为大图360*200，小图200*200
     * @param url 点击图文消息跳转链接
     * @return
     */
    public function responseNews($object, $articles)
    {
        $article_count = count($articles);
        $textTpl = "<xml>
                    <ToUserName><![CDATA[%s]]></ToUserName>
                    <FromUserName><![CDATA[%s]]></FromUserName>
                    <CreateTime>%s</CreateTime>
                    <MsgType><![CDATA[news]]></MsgType>
                    <ArticleCount>%s</ArticleCount>
                    <Articles>";
        $textTpl = sprintf($textTpl, $object->FromUserName, $object->ToUserName, time(), $article_count);
        $article_tpl = "<item>
                        <Title><![CDATA[%s]]></Title> 
                        <Description><![CDATA[%s]]></Description>
                        <PicUrl><![CDATA[%s]]></PicUrl>
                        <Url><![CDATA[%s]]></Url>
                        </item>";
        foreach ($articles as $value) {
            $article_str = sprintf($article_tpl, $value['title'], $value['description'], $value['picurl'], $value['url']);
            $textTpl .= $article_str;
        }
        $textTpl .= "</Articles>
                    </xml>";
        return $textTpl;
    }
    /**--------------------------------------------------------发送消息-被动回复用户消息 结束-------------------------------------------------------------**/

    /**
     * 客服接口-发消息
     * @param openid string 微信用户唯一凭证openid
     * @param type string 1 文本 2图文
     * @param data json 请求数据集
     * @return [type] [description]
     */
    public function custom_send($openid, $type, $msg, $customservice = '')
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' . $access_token['data'];
        $post_data['touser'] = $openid;
        $post_data['msgtype'] = $type;
        switch ($type) {
            case 'text'://发送文本消息
                $post_data['text']['content'] = $msg;
                break;
            case 'image'://发送图片消息
                $post_data['image']['media_id'] = $msg;
                break;
            case 'voice'://发送语音消息
                $post_data['voice']['media_id'] = $msg;
                break;
            case 'video'://发送视频消息,参数media_id,thumb_media_id,title,description
                $post_data['video'] = $msg;
                break;
            case 'music'://发送音乐消息,参数title,description,musicurl,hqmusicurl,thumb_media_id
                $post_data['music'] = $msg;
                break;
            case 'news'://发送图文消息（点击跳转到外链） 图文消息条数限制在8条以内，注意，如果图文数超过8，则将会无响应
                $post_data['news']['articles'] = $msg;
                break;
            case 'mpnews'://发送图文消息（点击跳转到图文消息页面） 图文消息条数限制在8条以内，注意，如果图文数超过8，则将会无响应
                $post_data['mpnews']['media_id'] = $msg;
                break;
            case 'wxcard'://发送卡券
                $post_data['wxcard']['card_id'] = $msg;
                break;
            case 'miniprogrampage'://发送小程序卡片,参数title,appid,pagepath,thumb_media_id
                $post_data['miniprogrampage'] = $msg;
                break;
            default:
                $post_data['msgtype'] = 'text';
                $post_data['text']['content'] = '这是一个默认图文消息:无消息类型';
                break;
        }
        //请注意，如果需要以某个客服帐号来发消息（在微信6.0.2及以上版本中显示自定义头像），则需在JSON数据包的后半部分加入customservice参数
        if (!empty($customservice)) {
            $post_data['customservice']['kf_account'] = $customservice;
        }
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->custom_send($openid, $type, $msg, $customservice);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }


    /**
     * 获取模板列表
     * @param access_token 微信调用接口唯一凭证
     * @return json
     */
    public function template_list()
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/template/get_all_private_template?access_token=' . $access_token['data'];
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        if (array_key_exists('errcode', $data)) {
            //access_token 过期重新获取并重新回调当前函数
            if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
                $access_token = $this->access_token(true);
                return $this->template_list();
            }
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }
        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }

    /**
     * 模板接口-发消息
     * @createtime 2017-08-01 15:50:11
     * @param data json 请求数据集
     * @return [type] [description]
     */
    public function template_send($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);

        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->template_send($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }
    /**********************消息管理  结束********/

    /**********************微信网页授权  开始********/

    /**
     * 通过微信授权code获取微信用户access_token
     * @param code 微信用户授权获取的code
     * @return json 返回结果
     */
    public function user_access_token($code)
    {

        //session开始
        if (!isset($_SESSION)) {
            session_start();
        }
        //如果code已经被使用过了返回缓存里面的微信用户access_token相关信息
        if (isset($_SESSION['user_access_token']) && $code == $_SESSION['user_access_token']['code']) {
            $data = $_SESSION['user_access_token'];
            $result = array('status' => 2, 'data' => $data);
            return $result;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $this->appid . '&secret=' . $this->appsecret . '&code=' . $code . '&grant_type=authorization_code';
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        //获取当前时间
        $time = time();
        if (array_key_exists('errcode', $data)) {
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }
        session_set_cookie_params(60 * 5); //设置access_token在服务器session中缓存时间
        //添加access_token过期时间
        $data['access_endtime'] = $time + $data['expires_in'];
        //添加refresh_token过期时间
        $data['refresh_endtime'] = $time + 60 * 60 * 24 * 30;
        $data['code'] = $code;
        $_SESSION['user_access_token'] = $data;
        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }

    //单发消息
    private function send($fakeid, $content)
    {
        $url = 'https://mp.weixin.qq.com/cgi-bin/singlesend?t=ajax-response&lang=zh_CN';
        $this->send_data = array(
            'type' => 1,
            'f' => 'json',
            'action' => 'sync',
            'content' => $content,
            'tofakeid' => $fakeid,
            'token' => $this->token,
            'ajax' => 1,
        );
        $this->referer = 'https://mp.weixin.qq.com/cgi-bin/singlemsgpage?token=' . $this->token . '&fromfakeid=' . $fakeid . '&msgid=&source=&count=20&t=wxm-singlechat&lang=zh_CN';
        return $this->curlPost($url);
    }

    //群发消息
    public function sendMessage($content = '', $userId = '')
    {
        if (is_array($userId) && !empty($userId)) {
            foreach ($userId as $v) {
                $json = json_decode($this->send($v, $content));
                if ($json->ret != 0) {
                    $errUser[] = $v;
                }
            }
        } else {
            foreach ($this->userFakeid as $v) {
                $json = json_decode($this->send($v['fakeid'], $content));
                if ($json->ret != 0) {
                    $errUser[] = $v['fakeid'];
                }
            }
        }
    }

    /**
     * 获取微信授权链接
     * @param string $redirect_uri 跳转地址
     * @param mixed $state 参数
     */
    public function get_authorize_url($redirect_uri = '', $state = '')
    {
        $redirect_uri = urlencode($redirect_uri);
        return "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appid}&redirect_uri={$redirect_uri}&response_type=code&scope=snsapi_base&state={$state}#wechat_redirect";
    }

    /**
     * 获取微信openid
     */
    public function getOpenid()
    {
        if (strpos($_SERVER['REQUEST_URI'], 'bindInfo')) {
            $action = isset($_GET['action']) ? $_GET['action'] : '';
            $direct_url = config('app.url') . "/api/bindInfo&action=".$action;
        } else if (strpos($_SERVER['QUERY_STRING'], 'detail')) {
            $account = isset($_GET['account']) ? $_GET['account'] : '';
            $direct_url = config('app.url') . "/api/recharge?action=detail&account=" . $account;
        } else if (strpos($_SERVER['REQUEST_URI'], 'recharge') && !strpos($_SERVER['REQUEST_URI'], 'rechargeDetail') && !strpos($_SERVER['QUERY_STRING'], 'detail')) {
            $direct_url = config('app.url') . "/api/recharge";
        } else {
            $direct_url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING'];
        }
        if (!isset($_GET['code'])) {
            //触发微信返回code码
            $url = $this->get_authorize_url($direct_url, $this->state);
            header('Location: ' . $url);
        } else {
            //获取code码，以获取openid
            $code = $_GET['code'];
            $access_info = $this->get_access_token($code);
            if ($access_info && isset($access_info['openid']) && $access_info['openid']) {
                $db_user_info = DB::table('wx_users')->where('openid', $access_info['openid'])->first(['i_id','nickname','headimgurl']);
                $user = $this->userinfo($access_info['access_token'], $access_info['openid']);
                if($user){
                    if (!$db_user_info) {
                        if ($user['status'] == 1) {
                            unset($user['data']['privilege']);
                            //注册新用户
                            DB::table('wx_users')->insert($user['data']);
                        }
                    } else if($user['data']['nickname']!=$db_user_info->nickname || $user['data']['headimgurl']!=$db_user_info->headimgurl){
                        //更新旧用户
                        DB::table('wx_users')->where('i_id',$db_user_info->i_id)->update(array('nickname'=>$user['data']['nickname'],'headimgurl'=>$user['data']['headimgurl']));
                    }
                }
            }
            return $access_info;
        }
    }

    /**
     * 获取授权token网页授权
     * @param string $code 通过get_authorize_url获取到的code
     */
    public function get_access_token($code = '')
    {
        $appid = $this->appid;
        $appsecret = $this->appsecret;
        $token_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" . $appid . "&secret=" . $appsecret . "&code=" . $code . "&grant_type=authorization_code";
        //echo $token_url;
        $res = $this->string_json($token_url);
        // var_dump( $token_data);
        if (isset($res['access_token'])) {

            return $res;
        }
        return $res;
    }

    /**
     * 刷新微信用户access_token
     * @param refresh_token 微信用户用来刷新access_token用的
     * @param state int 微信用户获取access_token的state
     * @return json 返回结果
     */
    public function user_refresh_token($refresh_token, $state = 0)
    {
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=' . $this->appid . '&grant_type=refresh_token&refresh_token=' . $refresh_token;
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        //获取当前时间
        $time = time();
        if (array_key_exists('errcode', $data)) {
            if ($data['errcode'] == 42002) {
                //如果超时重定向首页重新获取refresh_token
                header("location: https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . config('crm_config.wechat_appid') . "&redirect_uri=" . urlencode(config('crm_config.user_accesstoken_uri')) . "&response_type=code&scope=snsapi_base&state=" . $state . "#wechat_redirect");
                exit;
            }
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }
        //添加access_token过期时间
        $data['access_endtime'] = $time + $data['expires_in'];
        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }

    /**
     * 拉取用户信息(需scope为 snsapi_userinfo)
     * @param string access_token 微信用户授权获取的access_token
     * @param string openid 微信用户唯一凭证
     * @return json 返回结果
     */
    public function userinfo($access_token, $openid)
    {
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/sns/userinfo?access_token=' . $access_token . '&openid=' . $openid . '&lang=zh_CN';
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        if (array_key_exists('errcode', $data)) {
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }
        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }

    /**
     * 检验授权凭证（access_token）是否有效
     * @param string access_token 微信用户授权获取的access_token
     * @param string openid 微信用户唯一凭证
     * @return json 返回结果
     */
    public function auth($access_token, $openid)
    {
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/sns/auth?access_token=' . $access_token . '&openid=' . $openid;
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        //返回结果
        $status = $data['errcode'] == 0 ? 1 : 40014;
        $result = array('status' => $status, 'data' => $data);
        return $result;

    }
    /**********************微信网页授权  结束********/

    /**********************微信自定义分享  开始********/

    /**
     * 获取微信自定已分享配置参数包
     * @return array 配置参数包
     */
    public function getSignPackage()
    {
        $jsapiTicket = $this->getJsApiTicket();

        // 注意 URL 一定要动态获取，不能 hardcode.
        $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
        $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";

        $timestamp = time();
        $nonceStr = $this->createNonceStr();
        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket=" . $jsapiTicket . "&noncestr=" . $nonceStr . "&timestamp=" . $timestamp . "&url=" . $url;

        $signature = sha1($string);

        $signPackage = array(
            "appId" => $this->appid,
            "nonceStr" => $nonceStr,
            "timestamp" => $timestamp,
            "url" => $url,
            "signature" => $signature,
            "rawString" => $string
        );
        return $signPackage;
    }

    /**
     * 微信自定义分享随机字符串
     * @param length int 随机字符串长度
     * @return str string 随机字符串
     */
    private function createNonceStr($length = 16)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 微信自定义分享获取jsapiticket
     * @return jsapi_ticket string 微信jsapi调用凭证
     */
    private function getJsApiTicket()
    {
        // jsapi_ticket 应该全局存储与更新，以下代码以写入到文件中做示例
        $data = json_decode(file_get_contents(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/Json/jsapi_ticket.json"));
        if ($data->expire_time < time()) {
            $access_token = $this->access_token(false);
            if ($access_token['status'] != 1 && $access_token['status'] != 2) {
                return $access_token;
            }
            // 如果是企业号用以下 URL 获取 ticket
            // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=".$access_token['data'];
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=" . $access_token['data'];
            $res = json_decode($this->https_get($url));
            if (!isset($res->ticket)) {
                $access_token = $this->access_token(true);
                $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=" . $access_token['data'];
                $res = json_decode($this->https_get($url));
            }
            $ticket = $res->ticket;
            if ($ticket) {
                $data->expire_time = time() + 7200;
                $data->jsapi_ticket = $ticket;
                chmod(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/Json/jsapi_ticket.json", 0777);
                $fp = fopen(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . "/Json/jsapi_ticket.json", "w");
                fwrite($fp, json_encode($data));
                fclose($fp);
            }
        } else {
            $ticket = $data->jsapi_ticket;
        }

        return $ticket;
    }
    /**********************微信自定义分享  结束********/

    /**********************素材管理  开始********/

    /**
     * 获取永久素材列表
     * @param type string 素材的类型，图片（image）、视频（video）、语音 （voice）、图文（news）
     * @param offset int 从全部素材的该偏移位置开始返回，0表示从第一个素材 返回
     * @param count int 返回素材的数量，取值在1到20之间
     * @return [type] [description]
     */
    public function material_list($type, $offset = 0, $count = 20)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //组装参数
        $post_data['type'] = $type;
        $post_data['offset'] = $offset;
        $post_data['count'] = $count;
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if (isset($data['errcode']) && ($data['errcode'] == 42001 || $data['errcode'] == 40001)) {
            $access_token = $this->access_token(true);
            return $this->create_menu($type, $offset, $count);
        }
        //返回结果
        if (!isset($data['errcode'])) {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }
    /**********************素材管理  结束********/

    /**********************用户管理  开始********/
    /**
     * 获取关注用户个人信息
     * @param access_token string 微信调用接口唯一凭证
     * @param  openid string 用户的openid
     * @return json
     */
    public function user_info($openid = '')
    {
        //获取access_token
        $access_token = $this->access_token(false);

        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' . $access_token['data'] . '&openid=' . $openid . '&lang=zh_CN ';

        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        if (array_key_exists('errcode', $data)) {
            //access_token 过期重新获取并重新回调当前函数
            if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
                $access_token = $this->access_token(true);
                return $this->user_info($openid);
            }
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }

        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }

    /**
     * 批量获取用户信息
     * @param data json 请求数据集
     * @return [type] [description]
     */
    public function user_list_info($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if (isset($data['errcode']) && ($data['errcode'] == 42001 || $data['errcode'] == 40001)) {
            $access_token = $this->access_token(true);
            return $this->user_list_info($post_data);
        }
        //返回结果
        if (isset($data['user_info_list'])) {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 获取关注用户列表
     * @param access_token string 微信调用接口唯一凭证
     * @param  next_openid string 上次请求最后一个用户的openid
     * @return json
     */
    public function user_list($next_openid = '')
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }

        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/cgi-bin/user/get?access_token=' . $access_token['data'];
        if (!empty($next_openid)) {
            $url .= '&next_openid=' . $next_openid;
        }
        //获取请求结果,并将请求结果转换成json
        $data = $this->string_json($url);
        if (array_key_exists('errcode', $data)) {
            //access_token 过期重新获取并重新回调当前函数
            if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
                $access_token = $this->access_token(true);
                return $this->user_list($next_openid);
            }
            $result = array('status' => 40014, 'data' => $data);
            return $result;
        }
        //返回结果
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }
    /**********************用户管理  结束********/

    /**********************新版客服  开始********/

    /**
     * 添加客服账号
     * @param post_data 请求数据
     * @param kf_account string 完整客服帐号，格式为：帐号前缀@公众号微信号，帐号前缀最多10个字符，必须是英文、数字字符或者下划线，后缀为公众号微信号，长度不超过30个字符
     * @param nickname 客服昵称，最长16个字
     * @return [type] [description]
     */
    public function customservice_add($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/customservice/kfaccount/add?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->customservice_add($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 获取聊天记录
     * @param starttime int 起始时间，unix时间戳
     * @param endtime int 结束时间，unix时间戳，每次查询时段不能超过24小时
     * @param msgid int 消息id顺序从小到大，从1开始
     * @param number int 每次获取条数，最多10000条
     * @return json 返回结果
     */
    public function customservice_getmsglist($starttime, $endtime, $msgid, $number)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/customservice/msgrecord/getmsglist?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = array('starttime' => $starttime,
            'endtime' => $endtime,
            'msgid' => $msgid,
            'number' => $number);
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if (isset($data['errcode']) && ($data['errcode'] == 42001 || $data['errcode'] == 40001)) {
            $access_token = $this->access_token(true);
            return $this->customservice_getmsglist($starttime, $endtime, $msgid, $number);
        }
        //返回结果
        if (isset($data['recordlist'])) {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }
    /**********************新版客服  结束********/

    /**********************微信支付  开始********/

    /**
     * 统一下单，WxPayUnifiedOrder中out_trade_no、body、total_fee、trade_type必填
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param WxPayUnifiedOrder $inputObj
     * @param int $timeOut
     * @throws WxPayException
     * @return [type] [description]
     */
    public function unifiedOrder($data, $timeOut = 6)
    {
        //配置请求url
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        //检查请求参数
        if (!isset($data['out_trade_no'])) {
            $result = array('status' => 40010, 'data' => '缺少统一支付接口必填参数out_trade_no！');
            return $result;
        }
        if (!isset($data['body'])) {
            $result = array('status' => 40010, 'data' => '缺少统一支付接口必填参数body！');
            return $result;
        }
        if (!isset($data['total_fee'])) {
            $result = array('status' => 40010, 'data' => '缺少统一支付接口必填参数total_fee！');
            return $result;
        }
        if (!isset($data['trade_type'])) {
            $result = array('status' => 40010, 'data' => '缺少统一支付接口必填参数trade_type！');
            return $result;
        }
        if ($data['trade_type'] == 'JSAPI' && !isset($data['openid'])) {
            $result = array('status' => 40010, 'data' => '统一支付接口中，缺少必填参数openid！trade_type为JSAPI时，openid为必填参数！');
            return $result;
        }
        if ($data['trade_type'] == "NATIVE" && !isset($data['product_id'])) {
            $result = array('status' => 40010, 'data' => '统一支付接口中，缺少必填参数product_id！trade_type为NATIVE时，product_id为必填参数！');
            return $result;
        }

        //异步通知url未设置，则使用配置文件中的url
        if (!isset($data['notify_url'])) {
            $data['notify_url'] = config('app.wx_notify_url');
        }

        $data['appid'] = $this->appid;//微信支付分配的公众账号ID（企业号corpid即为此appId）
        $data['mch_id'] = $this->mch_id;//微信支付分配的商户号
        $data['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];//APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP
        $data['nonce_str'] = $this->createNonceStr(32);
        $data['sign'] = $this->makeSign($data);
        $xml = $this->arrayToXml($data);
        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }
        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }


    /**
     * 查询订单，WxPayOrderQuery中out_trade_no、transaction_id至少填一个
     *  appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    public function orderQuery($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/pay/orderquery";
        //检测必填参数
        if (!array_key_exists('out_trade_no', $data) && !array_key_exists('transaction_id', $data)) {
            $result = array('status' => 40010, 'data' => '订单查询接口中，out_trade_no、transaction_id至少填一个！');
            return $result;
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }
        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 关闭订单，WxPayCloseOrder中out_trade_no必填
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    public function closeOrder($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/pay/closeorder";
        //检测必填参数
        if (!array_key_exists('out_trade_no', $data)) {
            $result = array('status' => 40010, 'data' => '订单关闭接口中，out_trade_no必填！');
            return $result;
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array

        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }

        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;

    }

    /**
     * 申请退款，WxPayRefund中out_trade_no、transaction_id至少填一个且
     * out_refund_no、total_fee、refund_fee、op_user_id为必填参数
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    public function refund($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        //检测必填参数
        if (!array_key_exists('out_trade_no', $data) && !array_key_exists('transaction_id', $data)) {
            $result = array('status' => 40010, 'data' => '退款申请接口中，out_trade_no、transaction_id至少填一个！');
            return $result;
        }
        if (!array_key_exists('out_refund_no', $data)) {
            $result = array('status' => 40010, 'data' => '退款申请接口中，缺少必填参数out_refund_no！');
            return $result;
        }
        if (!array_key_exists('total_fee', $data)) {
            $result = array('status' => 40010, 'data' => '退款申请接口中，缺少必填参数total_fee！');
            return $result;
        }
        if (!array_key_exists('refund_fee', $data)) {
            $result = array('status' => 40010, 'data' => '退款申请接口中，缺少必填参数refund_fee！');
            return $result;
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        /**
         * 仅针对老资金流商户使用
         *REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款（默认使用未结算资金退款）
         *REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款
         */
        $data['refund_account'] = 'REFUND_SOURCE_RECHARGE_FUNDS';//退款资金来源
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, true, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }
        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 查询退款
     * 提交退款申请后，通过调用该接口查询退款状态。退款有一定延时，
     * 用零钱支付的退款20分钟内到账，银行卡支付的退款3个工作日后重新查询退款状态。
     * WxPayRefundQuery中out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    public function refundQuery($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/pay/refundquery";
        //检测必填参数
        if (!array_key_exists('out_refund_no', $data) && !array_key_exists('out_trade_no', $data) && !array_key_exists('transaction_id', $data) && !array_key_exists('refund_id', $data)) {
            $result = array('status' => 40010, 'data' => '退款查询接口中，out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个');
            return $result;
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }
        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 下载对账单，WxPayDownloadBill中bill_date为必填参数
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    public function downloadBill($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/pay/downloadbill";
        //检测必填参数
        if (!array_key_exists('bill_date', $data)) {
            $result = array('status' => 40010, 'data' => '对账单接口中，缺少必填参数bill_date！');
            return $result;
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }
        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 提交被扫支付API
     * 收银员使用扫码设备读取微信用户刷卡授权码以后，二维码或条码信息传送至商户收银台，
     * 由商户收银台或者商户后台调用该接口发起支付。
     * WxPayWxPayMicroPay中body、out_trade_no、total_fee、auth_code参数必填
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    public function micropay($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/pay/micropay";
        //检测必填参数
        if (!array_key_exists('body', $data)) {
            $result = array('status' => 40010, 'data' => '提交被扫支付API接口中，缺少必填参数body！');
            return $result;
        }
        if (!array_key_exists('out_trade_no', $data)) {
            $result = array('status' => 40010, 'data' => '提交被扫支付API接口中，缺少必填参数out_trade_no！');
            return $result;
        }
        if (!array_key_exists('total_fee', $data)) {
            $result = array('status' => 40010, 'data' => '提交被扫支付API接口中，缺少必填参数total_fee！');
            return $result;
        }
        if (!array_key_exists('auth_code', $data)) {
            $result = array('status' => 40010, 'data' => '提交被扫支付API接口中，缺少必填参数auth_code！');
            return $result;
        }
        //组装参数
        $data['spbill_create_ip'] = $this->get_client_ip();//终端ip
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }
        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 微信扫码支付模式一成功返回参数包装
     * @param $data 参数集
     * @return [type] [description]
     */
    public function native_pkg($data)
    {
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        return $data;
    }

    /**
     * 撤销订单API接口，WxPayReverse中参数out_trade_no和transaction_id必须填写一个
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    public function reverse($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
        //检测必填参数
        if (!array_key_exists('out_trade_no', $data) && !array_key_exists('transaction_id', $data)) {
            $result = array('status' => 40010, 'data' => '撤销订单API接口中，参数out_trade_no和transaction_id必须填写一个');
            return $result;
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }
        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 测速上报，该方法内部封装在report中，使用时请注意异常流程
     * interface_url、return_code、result_code、user_ip、execute_time_必填
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    private function report($data, $timeOut = 1)
    {
        $url = "https://api.mch.weixin.qq.com/payitil/report";
        //检测必填参数
        if (!array_key_exists('interface_url', $data)) {
            throw new Exception("接口URL，缺少必填参数interface_url！");
        }
        if (!array_key_exists('return_code', $data)) {
            throw new Exception("返回状态码，缺少必填参数return_code！");
        }
        if (!array_key_exists('result_code', $data)) {
            throw new Exception("业务结果，缺少必填参数result_code！");
        }
        if (!array_key_exists('user_ip', $data)) {
            throw new Exception("访问接口IP，缺少必填参数user_ip！");
        }
        if (!array_key_exists('execute_time_', $data)) {
            throw new Exception("接口耗时，缺少必填参数execute_time_！");
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['user_ip'] = $this->get_client_ip();//终端ip
        $data['time'] = date("YmdHis");//商户上报时间
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        return $response;
    }

    /**
     * 生成二维码规则,模式一生成支付二维码
     * appid、mchid、spbill_create_ip、nonce_str不需要填入2017-09-11 14:20:05
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    private function bizpayurl($data, $timeOut = 6)
    {

        //检测必填参数
        if (!array_key_exists('product_id', $data)) {
            $result = array('status' => 40010, 'data' => '生成二维码，缺少必填参数product_id！');
            return $result;
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['time_stamp'] = time();//时间戳
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $result = array('status' => 1, 'data' => $data);
        return $result;
    }

    /**
     * 转换短链接
     * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX)，
     * 减小二维码数据量，提升扫描速度和精确度。
     * appid、mchid、spbill_create_ip、nonce_str不需要填入
     * @param $data 参数集
     * @param $timeOut 超时
     * @return 成功时返回，其他抛异常
     */
    public function shorturl($data, $timeOut = 6)
    {
        $url = "https://api.mch.weixin.qq.com/tools/shorturl";
        //检测必填参数
        if (!array_key_exists('long_url', $data)) {
            $result = array('status' => 40010, 'data' => '需要转换的URL，签名用原串，传输需URL encode！');
            return $result;
        }
        //组装参数
        $data['appid'] = $this->appid;//公众账号ID
        $data['mch_id'] = $this->mch_id;//商户号
        $data['nonce_str'] = $this->createNonceStr(32);//随机字符串
        $data['sign'] = $this->makeSign($data);//签名
        $xml = $this->arrayToXml($data);

        $startTimeStamp = $this->milli_time();//请求开始时间
        $response = $this->postXml($xml, $url, false, $timeOut);
        $datas = $this->xmlToArray($response);//将xml转为array
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas);
            return $result;
        }
        //判断签名是否存在,及签名是否正确
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '签名错误！');
            return $result;
        }
        $this->reportCostTime($url, $startTimeStamp, $datas);//上报请求花费时间
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 支付结果通用通知
     * 直接回调函数使用方法: notify(you_function);
     * 回调类成员函数方法:notify(array($this, you_function));
     * $callback  原型为：function function_name($data){}
     * @param function $callback
     */
    public function notify()
    {
        //获取通知的数据
        $xml = file_get_contents('php://input');
        //将xml转为array
        $datas = $this->xmlToArray($xml);
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas['return_msg']);
            return $result;
        }
        //判断业务结果是否成功
        if ($datas['result_code'] != 'SUCCESS') {
            $result = array('status' => 40011, 'data' => $datas['err_code_des']);
            return $result;
        }
        //判断签名是否存在
        if (!array_key_exists('sign', $datas)) {
            $result = array('status' => 40011, 'data' => '缺少签名参数！');
            return $result;
        }
        //签名验证
        $data = $datas;
        unset($data['sign']);
        if ($this->makeSign($data) != $datas['sign']) {
            $result = array('status' => 40011, 'data' => '签名验证失败！');
            return $result;
        }
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 退款结果通知
     * @createtime 2017-09-22 17:10:21
     * @param array
     */
    public function refund_notify()
    {
        //获取通知的数据
        $xml = file_get_contents('php://input');
        log_result("//////////// start //////////////");
        log_result($xml);
        //将xml转为array
        $datas = $this->xmlToArray($xml);
        log_result("datas:" . json_encode($datas));
        //判断请求结果是否成功
        if ($datas['return_code'] != 'SUCCESS') {
            $result = array('status' => 40014, 'data' => $datas['return_msg']);
            log_result($datas['return_code'] != 'SUCCESS');
            return $result;
        }

        //对加密字符串进行解密
        $req_info = base64_decode($datas['req_info']);//对加密串A做base64解码，得到加密串B
        log_result("req_info:" . $req_info);
        $md5_key = md5($this->key);//对商户key做md5，得到32位小写key
        log_result("md5_key:" . $md5_key);
        $info = aes_decode($req_info, $md5_key);//用key*对加密串B做AES-256-ECB解密
        log_result("info:" . $info);
        $datas = array_merge($datas, $info);//合并数组 返回结果
        log_result("//////////// end //////////////");
        //返回结果
        $result = array('status' => 1, 'data' => $datas);
        return $result;
    }

    /**
     * 共享收货地址js函数需要的参数
     * @param
     * @return 获取共享收货地址js函数需要的参数，json格式可以直接做参数使用
     */
    public function editAddressParameters()
    {
        $data = array();
        $data["appid"] = $this->appid;
        $data["url"] = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
        $time = time();
        $data["timestamp"] = "$time";
        $data["noncestr"] = "1234568";
        $data["accesstoken"] = $this->access_token(false);
        ksort($data);
        $params = $this->toUrlParams($data);
        $addrSign = sha1($params);

        $afterData = array(
            "addrSign" => $addrSign,
            "signType" => "sha1",
            "scope" => "jsapi_address",
            "appId" => $this->appid,
            "timeStamp" => $data["timestamp"],
            "nonceStr" => $data["noncestr"]
        );
        $parameters = json_encode($afterData);
        return $parameters;
    }


    /**
     * 生成扫描支付URL,模式一
     * @createtime 2017-09-16 10:40:10
     * @param $productId 产品编号
     * @return [type] [description]
     */
    public function getPrePayUrl($productId)
    {
        $data['product_id'] = $productId;
        $result = $this->bizpayurl($data);
        if ($result['status'] != 1) {
            return $result;
        }
        $url = "weixin://wxpay/bizpayurl?" . $this->toUrlParams($result['data'], true);
        $result['data'] = $url;
        return $result;
    }


    /**
     * 生成二维码
     * @param $data 参数集
     * @return [type] [description]
     */
    public function qrcode($data)
    {
        error_reporting(E_ERROR);
        require_once 'phpqrcode/phpqrcode.php';
        $url = urldecode($data);
        header('Content-Type: image/png');
        ob_clean();
        $errorCorrectionLevel = "L"; // 纠错级别：L、M、Q、H
        $matrixPointSize = "4"; //生成图片大小 ：1到10
        \QRcode::png($url, false, $errorCorrectionLevel, $matrixPointSize);
        exit();
    }

    /**
     *  格式化参数格式化成url参数
     * @param $data 参数集
     * @param $sign 是否考虑sign
     * @return
     */
    private function toUrlParams($data, $sign = false)
    {
        $buff = "";
        foreach ($data as $k => $v) {
            if ($sign && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            } else {
                if ($k != "sign" && $v != "" && !is_array($v)) {
                    $buff .= $k . "=" . $v . "&";
                }
            }

        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 生成签名
     * @createtime 2017-09-11 10:01:14
     * @param
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    public function makeSign($data)
    {
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->toUrlParams($data);
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . $this->key;
        //签名步骤三：MD5加密
        $string = md5($string);
        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 以post方式提交xml到对应的接口url
     * @createtime 2017-09-11 10:08:11
     * @param string $xml 需要post的xml数据
     * @param string $url url
     * @param bool $useCert 是否需要证书，默认不需要
     * @param int $second url执行超时时间，默认30s
     * @return [type] [description]
     */
    private function postXml($xml, $url, $useCert = false, $second = 30)
    {

        header("Content-type: text/html; charset=utf-8");
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        //如果有配置代理这里就设置代理
        if (self::CURL_PROXY_HOST != "0.0.0.0" && self::CURL_PROXY_PORT != 0) {
            curl_setopt($ch, CURLOPT_PROXY, self::CURL_PROXY_HOST);
            curl_setopt($ch, CURLOPT_PROXYPORT, self::CURL_PROXY_PORT);
        }
        if ($useCert == true) {
            //设置证书
            //使用证书：cert 与 key 分别属于两个.pem文件
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, dirname(__FILE__) . self::SSLCERT_PATH);
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, dirname(__FILE__) . self::SSLKEY_PATH);
        }
        $data = curl_exec($ch);
        if (curl_errno($ch)) {
            return curl_error($ch);
        }
        curl_close($ch);
        return $data;

    }

    /**
     * 上报数据， 上报的时候将屏蔽所有异常流程
     * @param string $usrl
     * @param int $startTimeStamp
     * @param array $data
     * @return [type] [description]
     */
    private function reportCostTime($url, $startTimeStamp, $data)
    {
        //如果不需要上报数据
        if (self::REPORT_LEVENL == 0) {
            return;
        }
        //如果仅失败上报
        if (self::REPORT_LEVENL == 1 &&
            array_key_exists("return_code", $data) &&
            $data["return_code"] == "SUCCESS" &&
            array_key_exists("result_code", $data) &&
            $data["result_code"] == "SUCCESS") {
            return;
        }

        //上报逻辑
        $endTimeStamp = $this->milli_time();
        $result['interface_url'] = $url;
        $result['execute_time_'] = $endTimeStamp - $startTimeStamp;

        //返回状态码
        if (array_key_exists("return_code", $data)) {
            $result['return_code'] = $data["return_code"];
        }
        //返回信息
        if (array_key_exists("return_msg", $data)) {
            $result['return_msg'] = $data["return_msg"];
        }
        //业务结果
        if (array_key_exists("result_code", $data)) {
            $result['result_code'] = $data["result_code"];
        }
        //错误代码
        if (array_key_exists("err_code", $data)) {
            $result['err_code'] = $data["err_code"];
        }
        //错误代码描述
        if (array_key_exists("err_code_des", $data)) {
            $result['err_code_des'] = $data["err_code_des"];
        }
        //商户订单号
        if (array_key_exists("out_trade_no", $data)) {
            $result['out_trade_no'] = $data["out_trade_no"];
        }
        //设备号
        if (array_key_exists("device_info", $data)) {
            $result['device_info'] = $data["device_info"];
        }

        try {
            $this->report($result);
        } catch (Exception $e) {
            //不做任何处理
        }
    }
    /**********************微信支付  结束********/

    /**********************微信卡券  开始********/
    /**
     * 微信卡券获取api_ticket
     * @return api_ticket string 微信卡券调用凭证
     */
    private function api_ticket()
    {
        // api_ticket 应该全局存储与更新，以下代码以写入到文件中做示例

        $data = json_decode(file_get_contents(dirname(__FILE__) . "/json/api_ticket.json"));
        if ($data->expire_time < time()) {
            $access_token = $this->access_token(false);
            if ($access_token['status'] != 1 && $access_token['status'] != 2) {
                return $access_token;
            }
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card&access_token=" . $access_token['data'];
            $res = json_decode($this->https_get($url));
            if (!isset($res->ticket)) {
                $access_token = $this->access_token(true);
                $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=wx_card&access_token=" . $access_token['data'];
                $res = json_decode($this->https_get($url));
            }
            $ticket = $res->ticket;
            if ($ticket) {
                $data->expire_time = time() + 7200;
                $data->ticket = $ticket;
                $fp = fopen(dirname(__FILE__) . "/json/api_ticket.json", "w");
                fwrite($fp, json_encode($data));
                fclose($fp);
            }
        } else {
            $ticket = $data->ticket;
        }

        return $ticket;
    }

    /**
     * 卡券签名
     * @param
     * @return [type] [description]
     */
    public function card_sign($card_id)
    {
        $api_ticket = $this->api_ticket();
        $timestamp = time();
        $nonce_str = $this->createNonceStr();
        $arr = array($api_ticket, $timestamp, $nonce_str, $card_id);
        sort($arr, SORT_STRING);
        $signature = sha1(implode($arr));
        $signPackage = array(
            "timestamp" => $timestamp,
            "nonce_str" => $nonce_str,
            "signature" => $signature,
        );
        return $signPackage;
    }

    /**
     * 查看卡券详情
     * @param post_data 请求数据 卡券ID
     * @return json 请求结果
     */
    public function get_card($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/card/get?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->get_card($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 批量查询卡券列表
     * @param post_data 请求数据
     * offset 查询卡列表的起始偏移量，从0开始，即offset: 5是指从从列表里的第六个开始读取。
     * count 需要查询的卡片的数量（数量最大50）。
     * status_list 支持开发者拉出指定状态的卡券列表“CARD_STATUS_NOT_VERIFY”,待审核；“CARD_STATUS_VERIFY_FAIL”,审核失败；“CARD_STATUS_VERIFY_OK”，通过审核；“CARD_STATUS_DELETE”，卡券被商户删除；“CARD_STATUS_DISPATCH”，在公众平台投放过的卡券；
     * @return json 请求结果
     */
    public function card_list($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/card/batchget?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->get_card($post_data['card_id']);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }
    /**************************微信会员卡  开始****************************/

    /**
     * 拉取会员信息（积分查询）接口
     * @param card_id 查询会员卡的cardid
     * @param code 所查询用户领取到的code值
     * @return [type] [description]
     */
    public function membercard_info($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/card/membercard/userinfo/get?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->membercard_info($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 更新会员信息
     * @param
     * @return [type] [description]
     */
    public function membercard_update($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/card/membercard/updateuser?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->membercard_update($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 激活会员卡
     * @param
     * @return [type] [description]
     */
    public function membercard_active($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/card/membercard/activate?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->membercard_active($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 设置开卡字段接口
     * @param
     * @return [type] [description]
     */
    public function activateuserform($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/card/membercard/activateuserform/set?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->activateuserform($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**
     * 更改会员卡信息接口
     * @param 提交修改的数据集
     * @return [type] [description]
     */
    public function card_update($post_data)
    {
        //获取access_token
        $access_token = $this->access_token(false);
        if ($access_token['status'] != 1 && $access_token['status'] != 2) {
            return $access_token;
        }
        //配置请求url,加入access_token参数
        $url = 'https://api.weixin.qq.com/card/update?access_token=' . $access_token['data'];
        //将json数组转换成字符串数组
        $post_data = json_encode($post_data, JSON_UNESCAPED_UNICODE);
        //获取请求结果,并将请求结果转换成json
        $http_curl = new HttpCurlController();
        $http_curl->post($url, $post_data, 10);
        if ($http_curl->result === false) return false;
        $data = json_decode($http_curl->response, true);
        //access_token 过期重新获取并重新回调当前函数
        if ($data['errcode'] == 42001 || $data['errcode'] == 40001) {
            $access_token = $this->access_token(true);
            return $this->card_update($post_data);
        }
        //返回结果
        if ($data['errcode'] == 0 && $data['errmsg'] == 'ok') {
            $result = array('status' => 1, 'data' => $data);
            return $result;
        }
        $result = array('status' => 40014, 'data' => $data);
        return $result;
    }

    /**************************微信会员卡  结束****************************/
    /**********************微信卡券  结束********/

    public function string_json($url)
    {
        $http_curl = new HttpCurlController();
        $http_curl->get($url, 10);
        if ($http_curl->result === false)
            return "";
        $res = json_decode($http_curl->response, true);
        return $res;
    }

    public function https_get($url)
    {
        $http_curl = new HttpCurlController();
        $http_curl->get($url, 10);
        if ($http_curl->result === false)
            return "";
        return $http_curl->response;
    }

    public function addressProcess()
    {

        $appId = $this->appid;
        $nonceStr = $this->createNoncestr(12);
        $timeStamp = time();
        $timeStamp = "$timeStamp";//时间戳必须是字符串
        // 计算addrSign:包括appid，url，timestamp，noncestr，网页accesstoken
        $url = $_POST['currentURL'];
        $data = $this->access_token('accessToken');
        if (!$data) {
            return '';
        }
        $accessToken = $data['data'];
        $string = "accesstoken=$accessToken&appid=$appId&noncestr=$nonceStr&timestamp=$timeStamp&url=$url";
        $addrSign = sha1($string);
        $signPackage = array(
            "appId" => $this->appid,
            "nonceStr" => $nonceStr,
            "timeStamp" => $timeStamp,
            "addrSign" => $addrSign
        );
        return $signPackage;
    }

    public static function arrayToXml($arr)
    {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }

        $xml .= "</xml>";
        return $xml;
    }

    public static function xmlToArray($xml)
    {

        //将XML转为array
        libxml_disable_entity_loader(true);
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }

    public function milli_time()
    {
        return strval(time());
    }

    public function get_client_ip()
    {
        return $_SERVER['REMOTE_ADDR'];
    }

    public static function makePayOrderId()
    {
        list($ms, $s) = explode(' ', microtime());
        $ms = sprintf("%03d", $ms * 1000);
        return time() . $ms . rand(1000000, 9999999);
    }
}